/**
 ******************************************************************************
 * @file    main.c
 * @author  MCU Team
 * @Version V1.0.0
 * @Date    2022/01/19
 * @brief   main function
 ******************************************************************************
 */

#include "zb32l03x_hal.h"
#include "Ringbuff.h"
#include "log.h"
#include "bsp_lcd.h"
#include "bsp_loop.h"
#include "app_flash.h"
#include "app_key.h"
#include "app_beep.h"
#include "app_lcd.h"
#include "app_powerctrl.h"
#include "bsp_loop.h"
#include "app_loop.h"
#include "tty.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"
#include "user_eventflag.h"
#include "ui_lcd.h"

#define TTY_TASK_STACKSIZE                    configMINIMAL_STACK_SIZE
#define TTY_TASK_PRIORITY                     1  

#define SCAN_TASK_STACKSIZE                   configMINIMAL_STACK_SIZE  
#define SCAN_TASK_PRIORITY                    2      

#define LOOP_TASK_STACKSIZE                   configMINIMAL_STACK_SIZE  
#define LOOP_TASK_PRIORITY                    3     

#define START_TASK_STACKSIZE                  configMINIMAL_STACK_SIZE  
#define START_TASK_PRIORITY                   4  


PRIVILEGED_DATA TaskHandle_t ttytask_handler = NULL;
PRIVILEGED_DATA TaskHandle_t scantask_handler = NULL;
PRIVILEGED_DATA TaskHandle_t looptask_handler = NULL;
PRIVILEGED_DATA TaskHandle_t starttask_handler = NULL;
/* Track how many times tick interrupt has occurred. */
static unsigned int uTickInterruptCounter = 0;
EventGroupHandle_t xEventGroup;
loopctrlinfo_t loopctrlinfo;


ui_t uitcb;

/*-----------------------------------------------------------*/

static void prvInitializeHeap( void )
{
	/* Place the first block of the heap memory in the first bank of RAM. */
	static uint8_t ucHeap1[ configTOTAL_HEAP_SIZE ];
	
	/* Memory regions are defined in address order, and terminate with NULL. */
	static HeapRegion_t xHeapRegions[] =
	{
		{ ( unsigned char * ) ucHeap1, sizeof( ucHeap1 ) },
		{ NULL,                        0                 }
	};

	vPortDefineHeapRegions( xHeapRegions );

	return;
}
/*-----------------------------------------------------------*/

void vApplicationMallocFailedHook( void )
{
	/* vApplicationMallocFailedHook() will only be called if
	configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h.  It is a hook
	function that will get called if a call to pvPortMalloc() fails.
	pvPortMalloc() is called internally by the kernel whenever a task, queue,
	timer or semaphore is created.  It is also called by various parts of the
	demo application.  If heap_1.c or heap_2.c are used, then the size of the
	heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
	FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
	to query the size of free heap space that remains (although it does not
	provide information on how the remaining heap might be fragmented). */
	taskDISABLE_INTERRUPTS();
	for( ;; );
}

/*-----------------------------------------------------------*/

void vApplicationIdleHook( void )
{
	/* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
	to 1 in FreeRTOSConfig.h.  It will be called on each iteration of the idle
	task.  It is essential that code added to this hook function never attempts
	to block in any way (for example, call xQueueReceive() with a block time
	specified, or call vTaskDelay()).  If the application makes use of the
	vTaskDelete() API function (as this demo application does) then it is also
	important that vApplicationIdleHook() is permitted to return to its calling
	function, because it is the responsibility of the idle task to clean up
	memory allocated by the kernel to any task that has since been deleted. */
}

/*-----------------------------------------------------------*/

void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
	( void ) pcTaskName;
	( void ) pxTask;

	/* Run time stack overflow checking is performed if
	configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  This hook
	function is called if a stack overflow is detected. */
	taskDISABLE_INTERRUPTS();
	for( ;; );
}

/*-----------------------------------------------------------*/

void vApplicationTickHook( void )
{
#if mainCHECK_INTERRUPT_STACK == 1
extern unsigned long _pvHeapStart[];

	/* This function will be called by each tick interrupt if
	configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h.  User code can be
	added here, but the tick hook is called from an interrupt context, so
	code must not attempt to block, and only the interrupt safe FreeRTOS API
	functions can be used (those that end in FromISR()). */

	/* Manually check the last few bytes of the interrupt stack to check they
	have not been overwritten.  Note - the task stacks are automatically
	checked for overflow if configCHECK_FOR_STACK_OVERFLOW is set to 1 or 2
	in FreeRTOSConifg.h, but the interrupt stack is not. */
	configASSERT( memcmp( ( void * ) _pvHeapStart, ucExpectedInterruptStackValues, sizeof( ucExpectedInterruptStackValues ) ) == 0U );
#endif /* mainCHECK_INTERRUPT_STACK */

	uTickInterruptCounter++;
}

/**
 *  This function is executed in case of error occurrence.
 */
void Error_Handler(void)
{
    __BKPT(1);
}

//=============================================================================
//                  Private Function Definition
//=============================================================================
/**
  * @brief System Clock Configuration
  * @retval None
  */
static void _SystemClock_Config(void)
{
#define __GET_HIRC_CLK_MHZ(x)            (((x) == RCC_HIRCCALIBRATION_24M) ? 24 :    \
                                          ((x) == RCC_HIRCCALIBRATION_22M) ? 22 :    \
                                          ((x) == RCC_HIRCCALIBRATION_16M) ? 16 :    \
                                          ((x) == RCC_HIRCCALIBRATION_8M)  ? 8 : 4)

    HAL_StatusTypeDef       rval = HAL_OK;
    RCC_OscInitTypeDef      RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef      RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType            = RCC_OSCILLATORTYPE_HIRC;
    RCC_OscInitStruct.HIRCState                 = RCC_HIRC_ON;
    RCC_OscInitStruct.HXTState                  = RCC_HXT_OFF;
    RCC_OscInitStruct.HIRCCalibrationValue      = RCC_HIRCCALIBRATION_24M;
    RCC_OscInitStruct.HIRCDivider               = RCC_HIRC_DIV1;

#if defined(CONFIG_USE_ZB32L032)
    RCC_OscInitStruct.PLL.PLLState              = RCC_PLL_ON;
    //RCC_OscInitStruct.PLL.PLLSource             = RCC_PLLSOURCE_XTAL;
    RCC_OscInitStruct.PLL.PLLSource             = RCC_PLLSOURCE_HIRC;

    if( RCC_OscInitStruct.PLL.PLLState == RCC_PLL_ON )
    {
        RCC_OscInitStruct.OscillatorType            |= (RCC_OSCILLATORTYPE_PLL);

        RCC_OscInitStruct.PLL.PLLCalibrationValue   = RCC_PLLCALIBRATION;

        RCC_ClkInitStruct.SYSCLKSource              = RCC_SYSCLKSOURCE_PLL;
    }

    switch( RCC_OscInitStruct.PLL.PLLSource )
    {
        default:
        case RCC_PLLSOURCE_HIRC:
            RCC_OscInitStruct.PLL.PLLSourceMHz        = __GET_HIRC_CLK_MHZ(RCC_OscInitStruct.HIRCCalibrationValue);

            if( RCC_OscInitStruct.PLL.PLLSourceMHz == 24 ||
                RCC_OscInitStruct.PLL.PLLSourceMHz == 22 )
                RCC_OscInitStruct.PLL.PLLSourceDiv = RCC_PLLSource_PreDiv6;
            else if( RCC_OscInitStruct.PLL.PLLSourceMHz == 16 )
                RCC_OscInitStruct.PLL.PLLSourceDiv = RCC_PLLSource_PreDiv4;
            else if( RCC_OscInitStruct.PLL.PLLSourceMHz == 8 )
                RCC_OscInitStruct.PLL.PLLSourceDiv = RCC_PLLSource_PreDiv2;
            else
                RCC_OscInitStruct.PLL.PLLSourceDiv = RCC_PLLSource_PreDiv1;

            RCC_OscInitStruct.PLL.PLLMul              = RCC_PLL_MUL32;
            RCC_OscInitStruct.PLL.PLLSysClkDivider    = RCC_PLLSys_Div2; // PLL_SYS = 64MHz
            break;
        case RCC_PLLSOURCE_XTAL:
        case RCC_PLLSOURCE_OSC:
            RCC_OscInitStruct.PLL.PLLSourceMHz        = HXT_VALUE / 1000000;
            RCC_OscInitStruct.PLL.PLLSourceDiv        = RCC_PLLSource_PreDiv2;
            RCC_OscInitStruct.PLL.PLLMul              = RCC_PLL_MUL32;
            RCC_OscInitStruct.PLL.PLLSysClkDivider    = RCC_PLLSys_Div3; // PLL_SYS = 64MHz
            break;
    }
#endif  /* CONFIG_USE_ZB32L032 */

    rval = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    if( rval != HAL_OK )
    {
        Error_Handler();
    }

    /* Initializes the CPU, AHB and APB busses clocks */
    RCC_ClkInitStruct.ClockType     = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APBCLKDivider = RCC_PCLK_DIV1;

    rval = HAL_RCC_ClockConfig(&RCC_ClkInitStruct);
    if( rval != HAL_OK )
    {
        Error_Handler();
    }
    return;
}



void task_scan( void * para)
{
  static KEY_t keytcb[KEY_NUM];
  para=para;
  app_key_init(keytcb);
  ui_lcd_init(&uitcb);
  msg("scan task\r\n");
  while (1)
  {
    app_key_process(keytcb);
    ui_lcd_process(&uitcb);
    vTaskDelay(10);
  }
  
}

void tty_scan(void *para)
{
  para = para;
  uint32_t ulNotificationValue;
  app_beep_init();
  while (1)
  {
    ulNotificationValue = ulTaskNotifyTake(pdTRUE, 10);
    if (ulNotificationValue & NOTIFY_TTYTASK_REFLASHLCD)
    {
        app_lcd_refresh_gram();
    }
    
    app_beep_pro();
    tty_process();
    //app_powerctrl_process();
    
  }
}

void loop_task(void *para)
{
  para = para;
  EventBits_t event;
  uint32_t ulNotificationValue;
  //解码相关
  uint32_t decoderesult;

  app_loop_init();
  msg("loop task\r\n");
  while (1)
  {
    event =  xEventGroupWaitBits(xEventGroup,EVENTGROUP_LOOPSEND_START,pdTRUE,pdTRUE,portMAX_DELAY);
    if (event& EVENTGROUP_LOOPSEND_START)
    {
        app_loop_sendinfotobit(&loopctrlinfo.sendinfo, &loopctrlinfo);
        app_loop_decodebuffsampleinit(&loopctrlinfo);
        //HAL_GPIO_WritePin(BSP_LOOP_SAMPLE_TEST_PORT, BSP_LOOP_SAMPLE_TEST_PIN,GPIO_PIN_SET);
        app_loop_sendcodestart(&loopctrlinfo);
        while(1)
        {
            //等待解码
            ulNotificationValue=ulTaskNotifyTake(pdTRUE,10);
            if (ulNotificationValue & NOTIFY_LOOPTASK_BCAKCODESAMPLE_FINISH) // 一个bit采样完成
            {
               
  
            }
            //可以解码
            if(ulNotificationValue & NOTIFY_LOOPTASK_DECODE)
            {
               
               decoderesult=app_loop_decode(&loopctrlinfo);
               loopctrlinfo.sendinfo.backcodecheck=(decoderesult>>1)&0x01;
               loopctrlinfo.sendinfo.online=(decoderesult>>2)&0x01;
               loopctrlinfo.sendinfo.backcodedate=(decoderesult>>3)&0xffff;
               if (loopctrlinfo.sendinfo.online == 0)
               {
                   loopctrlinfo.sendinfo.backcodecheck=0;
               }
               xEventGroupSetBits(xEventGroup,EVENTGROUP_LOOPDECODE_END);

               //msg("decoderesult=0x%x\r\n",decoderesult);
               //msg("backcodedate=0x%x\r\n",loopctrlinfo.sendinfo.backcodedate);
               //msg("online=0x%x\r\n",loopctrlinfo.sendinfo.online);
               //msg("backcodecheck=0x%x\r\n",loopctrlinfo.sendinfo.backcodecheck);
               //msg("**********\r\n");
            }
            //总线发码完成
            if(ulNotificationValue & NOTIFY_LOOPTASK_SENDDATEBIT)
            {
              bsp_loop_backcodesamplestop();
              xEventGroupSetBits(xEventGroup,EVENTGROUP_LOOPSEND_END);
              break;
            }
           
        }  
    }
   
  }
}


void start_task(void *para)
{
    para = para;
    while (1)
    {
        vTaskDelay(3000);
        xEventGroup = xEventGroupCreate();

        xTaskCreate(task_scan,           /* Function that implements the task. */
                    "scan",              /* Text name of the task. */
                    SCAN_TASK_STACKSIZE, /* Stack allocated to the task. */
                    NULL,                /* The task parameter is not used. */
                    SCAN_TASK_PRIORITY,  /* The priority to assign to the task. */
                    &scantask_handler);

        xTaskCreate(tty_scan,           /* Function that implements the task. */
                    "tty",              /* Text name of the task. */
                    TTY_TASK_STACKSIZE, /* Stack allocated to the task. */
                    NULL,               /* The task parameter is not used. */
                    TTY_TASK_PRIORITY,  /* The priority to assign to the task. */
                    &ttytask_handler);

        xTaskCreate(loop_task,           /* Function that implements the task. */
                    "loop",              /* Text name of the task. */
                    LOOP_TASK_STACKSIZE, /* Stack allocated to the task. */
                    NULL,                /* The task parameter is not used. */
                    LOOP_TASK_PRIORITY,  /* The priority to assign to the task. */
                    &looptask_handler);
        app_powerctrl_systempower(1);
        
        vTaskDelete(NULL);
    }
}

//=============================================================================
//                  Public Function Definition
//=============================================================================
/**
 *  The application entry point.
 */
int main(void)
{
    /* Configure the system clock */
    _SystemClock_Config();
    tty.init(115200);
    app_powerctrl_init();

    
    
    prvInitializeHeap();

    
    
     xTaskCreate(start_task,			/* Function that implements the task. */
					"start", 				/* Text name of the task. */
					START_TASK_STACKSIZE,		/* Stack allocated to the task. */
					NULL, 					/* The task parameter is not used. */
					START_TASK_PRIORITY, 		/* The priority to assign to the task. */
					&starttask_handler);	
				
    /* Start the kernel.  From here on, only tasks and interrupts will run. */
    vTaskStartScheduler();

    /* If all is well, the scheduler will now be running, and the following
    line will never be reached.  If the following line does execute, then there
    was	insufficient FreeRTOS heap memory available for the idle and/or timer
    tasks to be created.  See the memory management section on the FreeRTOS web
    site, or the FreeRTOS tutorial books for more details. */
    for (;;)
        ;
}

#ifdef  USE_FULL_ASSERT

/**
 *  @brief  Reports the name of the source file and the source line number
 *              where the assert_param error has occurred.
 *
 *  @param [in] func_name       Pointer to a function name
 *  @param [in] line            assert_param error line source number
 *  @return
 *      None
 */
void assert_failed(const char *func_name, uint32_t line)
{
    /**
     *  User can add his own implementation to report the file name and line number,
     *  e.g. printf("Wrong parameters value: func %s on line %d\n", file, line)
     */
}
#endif /* USE_FULL_ASSERT */


